package functions;

import java.util.ArrayList;

public class Function extends FunctionIF implements Cloneable {
	ArrayList<ArrayList<FunctionIF>> sumOfProduct;

	public Function()
	{
		sumOfProduct = new ArrayList<ArrayList<FunctionIF>>();
		expon = 1;
		coff = 1; // TODO Check this
	}
	public Function(ArrayList<ArrayList<FunctionIF>> productSum)
	{
		sumOfProduct = productSum;
		expon = 1;
		coff = 1; // TODO Check this
	}

	@Override
	public double eval(double x)
	{
		double ret = 0; // return value
		for (ArrayList<FunctionIF> productTerm : sumOfProduct)
		{
			double res = 1;
			// will multiply terms
			for (FunctionIF func : productTerm)
				res *= func.eval(x);

			// adding this multiplication to the return value
			ret += res;
		}
		return coff*Math.pow(ret, expon);
	}

	@Override
	public FunctionIF diff()
	{
		if (expon == 0)
			return new PolynTerm(1, 0);

		Function diff = new Function();

		// parsing (#### + ##### + ####) .........summation
		for (ArrayList<FunctionIF> productTerm : sumOfProduct)
		{
			// Differentiate #*#*#*# ..........product of terms
			for (int i = 0; i < productTerm.size(); i++)
			{
				int j = 0;
				ArrayList<FunctionIF> newTerm = new ArrayList<FunctionIF>();
				for (FunctionIF func : productTerm)
				{
					if (j != i)
					{
						// other terms will be cloned and added to the new
						// function
						newTerm.add((FunctionIF) func.clone());
					} else
					{
						// the term that will be differentiated
						if (func.expon != 0)
							newTerm.add(func.diff());
					}
					j++;
				}
				if(!newTerm.isEmpty())
					diff.addProductTerm(newTerm);
			}
		}
		if (expon != 1)
		{
			// will have to multiple terms as there's exponent
			ArrayList<FunctionIF> product = new ArrayList<FunctionIF>();
			FunctionIF df = diffExponent();
			product.add(df); // Differentating the function (exponent part)

			product.add(diff);
			Function f = new Function();
			f.addProductTerm(product);
			return f;
		} else
			return diff;
	}

	public void addProductTerm(ArrayList<FunctionIF> product)
	{
		sumOfProduct.add(product);
	}

	@Override
	@SuppressWarnings("unchecked")
	protected Object clone()
	{
		ArrayList<ArrayList<FunctionIF>> productSum = (ArrayList<ArrayList<FunctionIF>>) sumOfProduct
				.clone();
		Function f = new Function(productSum);
		return f;
	}

	@Override
	public String toString()
	{
		String s = coff + "(";
		int x = 0;
		for (ArrayList<FunctionIF> term : sumOfProduct)
		{
			if (++x < sumOfProduct.size())
				s += termString(term) + " + ";
			else
				s += termString(term)  + ")^" + expon;
		}
		return s;
	}

	private String termString(ArrayList<FunctionIF> term)
	{
		String s = "";
		for(int i= 0 ;i  <term.size();i++)
			if(i < term.size()-1)
				s+=term.get(i)+"*";
			else
				s+=term.get(i);
		return s;
	}
	/**
	 * returns the number of sum of products
	 * 
	 * @return
	 */
	public int size()
	{
		return sumOfProduct.size();
	}
}
